#pragma once
#include <sstream>
#include <memory>
#include "compiler.hpp"
#include "runner.hpp"
#include <jsoncpp/json/json.h>
#include "comm/util.hpp"
#include "comm/log.hpp"

using namespace ns_util;
using namespace ns_log;
using namespace ns_cpmpiler;
using namespace ns_runner;

class CompilerAndRun
{

public:
    /*

        @param in_json {"code:$include...", "input":"", "cpu_limit" : 1, "memlimit":1024}
        @param out_json {"status" : 0, "reason" : "", "stdout" : "", "stderr" : ""}

    */
    static void Start(const std::string &in_json, std::string *out_json)
    {
        Json::Value inValue;
        Json::Reader reader;
        reader.parse(in_json, inValue); // 最后再处理差错问题

        std::string code = inValue["code"].asString();
        auto input = inValue["input"].asString();
        int cpuLimit = inValue["cpu_limit"].asInt();
        int memLimit = inValue["mem_limit"].asInt();

        int status_code = 0;
        int run_result = 2;
        Json::Value out_value;
        std::string fileName; // 需要内部定义的唯一文件名
        if (code.size() == 0)
        {
            // 最后进行出错处理
            // out_value["status"] = -1;
            // out_value["reason"] = "用户提交的代码为空";
            LOG(WARNING) << "Inputed code is empty!" << std::endl;
            status_code = -1; // 代码为空
            goto end;
        }

        fileName = FileUtil::uniqFileName();
        if (!FileUtil::writeFile(PathUtil::Src(fileName), code)) // 形成的临时src文件
        {
            // out_value["status"] = -2;
            // out_value["reaon"] = "发生了未知错误";
            status_code = -2; // 未知错误
            goto end;
            // return;
        }
        if (!Compiler::Compile(fileName))
        {
            // out_value["status"] = -3; // 代码编译时出错！
            // out_value["reason"] = FileUtil::readFile(PathUtil::CompilerError(fileName));
            // return;
            status_code = -3;
            goto end;
        }

        run_result = Runner::runner(fileName, cpuLimit, memLimit);
        if (run_result < 0)
        {
            // out_value["status"] = -2;
            // out_value["reaon"] = "发生了未知错误";
            // return;

            status_code = -2;
            goto end;
        }
        else if (run_result > 0)
        {
            // out_value["status"]= code; /*运行报错*/
            // out_value["reason"] = signoToDesc(run_result);
            // 程序运行崩溃
            status_code = run_result;
        }
        else
        {
            // 运行成功
            status_code = 0;
        }
    end:
        out_value["status"] = status_code;
        out_value["reason"] = codeToDesc(status_code, fileName);
        if (status_code == 0)
        {
            // 整个过程运行成功
            std::string _stdout, _stderr;
            out_value["stdout"] = FileUtil::readFile(PathUtil::Stdout(fileName), &_stdout, true) ? _stdout : "Unkown Error";
            out_value["stderr"] = FileUtil::readFile(PathUtil::Stderr(fileName), &_stderr, true) ? _stderr : "Unkown Error";
        }

        Json::StreamWriterBuilder builder;
        builder["emitUTF8"] = true;
        std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
        std::stringstream ss;
        writer->write(out_value, &ss);
        *out_json = ss.str();
        FileUtil::removeTempFile(fileName);
    }

    static std::string codeToDesc(int code, const std::string &fileName)
    {
        std::string desc;
        switch (code)
        {
        case 0:
            desc = "编译运行成功";
            break;
        case -1:
            desc = "用户代码提交为空";
            break;
        case -2:
            desc = "未知错误";
            break;
        case -3:
            if (!FileUtil::readFile(PathUtil::CompilerError(fileName), &desc, true))
            {
                LOG(ERROR) << "获取信息错误" << std::endl;
            }
            break;
        case SIGABRT:
            desc = "内存超过范围";
            break;
        case SIGXCPU:
            desc = "cpu使用超时";
            break;
        case SIGFPE:
            desc = "浮点数溢出";
            break;
        default:
            desc = "未知错误" + std::to_string(code);
            break;
        }
        return desc;
    }
};